package com.plugins.task;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 *  异步线程执行器
 *   这个用来处理不用管控的线程 使用起来简单一些
 */
public class TaskManager implements Runnable {
    private static final String TAG = TaskManager.class.getSimpleName();
    private static final int COMMON_EXCUTE_TASK_TYPE = 0;
    public volatile boolean isRunning = false;
    private boolean isHasInit = false;
    private static final int DEFAULT_THREAD_NUM = 5;
    private int threadNum = DEFAULT_THREAD_NUM;
    private static ScheduledExecutorService singlePool = null;
    private static ExecutorService threadPool = null;
    private static ConcurrentLinkedQueue<Task> allTask = null;
    private static ConcurrentHashMap<Integer, Object> uniqueListenerList = null;
    public Handler getHandler() {
        return handler;
    }
    public int getThreadNum() {
        return threadNum;
    }
    public boolean isHasInit() {
        return isHasInit;
    }
    public boolean isRunning() {
        return isRunning;
    }
    private final static Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            long start = System.currentTimeMillis();
            switch (msg.what) {
                case COMMON_EXCUTE_TASK_TYPE:
                    if (msg != null && msg.obj != null
                            && msg.obj instanceof Task) {
                        TaskManager.getInstance().doCommonHandler((Task) msg.obj);
                    }
                    break;
            }
            long end = System.currentTimeMillis();
            Log.i(TAG," handleMessage 总共消耗时间为：" + (end - start));
        }
    };
    private static TaskManager instance = null;
    private TaskManager() {
        Log.i(TAG,"TaskManager 当前的线程Id为：" + Thread.currentThread().getId());
    }
    public static TaskManager getInstance() {
        if (instance == null) {
            synchronized (TaskManager.class) {
                if (instance == null) {
                    instance = new TaskManager();
                }
            }
        }
        return instance;
    }
    /**
     * 初始化操作，这个主要是初始化需要执行异步
     * 回调任务的线程池，默认开启5个线程
     */
    public void init() {
        init(threadNum);
    }
    /**
     * 初始化操作，这个主要是初始化需要执行异步
     * 回调任务的线程池，可以传入线程的个数
     */
    public synchronized void init(int initNum) {
        if (!isHasInit) {
            /**
             * 初始化之后就相当于开始了线程次的运行
             * 只不过如果没有任务处于等待状态
             */
            isRunning = true;
            if (initNum > 0) {
                threadNum = initNum;
            }
            threadPool = Executors.newFixedThreadPool(threadNum);
            singlePool = Executors.newSingleThreadScheduledExecutor();
            allTask = new ConcurrentLinkedQueue<>();
            uniqueListenerList = new ConcurrentHashMap<>();

            /**
             * 初始化需要用到的线程
             */
            for (int i = 0; i < threadNum; i++) {
                threadPool.execute(this);
            }
            isHasInit = true;
        } else {
            Log.d(TAG,"TaskManager 已经初始化完成,不需要重复初始化");
        }
    }


    /**
     * 当应用被销毁时，执行清理操作
     */
    public void doDestory() {
        /**
         * 关闭线程开关
         */
        isRunning = false;
        isHasInit = false;
        if (allTask != null) {
            allTask.clear();
            allTask = null;
        }
        if (uniqueListenerList != null) {
            uniqueListenerList.clear();
            uniqueListenerList = null;
        }
        if (threadPool != null) {
            threadPool.shutdown();
            threadPool = null;
        }
        if (singlePool != null) {
            singlePool.shutdown();
            singlePool = null;
        }
    }

    /**
     * 向任务队列中添加任务对象,添加成功后,
     * 任务会自动执行,执行完事儿后,不进行任何回调操作
     *
     * @param task 可执行的任务对象
     */
    public void exec(Task task) {
        if (task != null) {
            allTask.offer(task);
            synchronized (allTask) {
                allTask.notifyAll();
            }
        }
    }

    /**
     * 这个方法主要是获取普通的回调数据,
     * 获取成功后会把加入的 Task 对象回调到用户界面
     *
     * @param task     加入的任务Task
     * @param callback 任务的回调接口GetDataCallback
     */
    public void exec(Task task, OnResult callback) {
        /**
         *  把CallBack 接口加入列表中,用完之后移除
         */
        try {
            if (task != null && callback != null) {
                if (task.getUniqueID() == 0) {
                    task.setUniqueID(task.hashCode());
                }
                uniqueListenerList.put(task.getUniqueID(), callback);
                exec(task);
            }
        } catch (Exception e) {
            // TODO: handle exception
            /**
             * 其实，这个地方的数据应该写到一个文件中
             */
            //LogUtils.e("TaskManager========getData=====" + e.toString() + " thread id 为：" + Thread.currentThread().getId());
            e.printStackTrace();
        }
    }

    /**
     * 从任务队列中移除任务对象,使其不再执行(如果任务已经执行,则此方法无效)
     * @param task 添加的任务对象
     */
    public void removeTask(Task task) {
        if (task != null) {
            uniqueListenerList.remove(task.getUniqueID());
            allTask.remove(task);
        }
    }
    public void log() {
        // Log.i("TaskManager", "allTask " + allExecuteTask.size() + " uniqueListenerList " + uniqueListenerList.size());
    }
    /**
     * 清除所有的任务
     */
    public void removeAllTask() {
        allTask.clear();
        uniqueListenerList.clear();
    }
    /**
     * 所有的异步任务都在此执行
     */
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while (isRunning) {
            /**
             * 从allTask取任务
             */
            Task lastTask = allTask.poll();
            if (lastTask != null) {
                try {
                    /**
                     * 真正开始执行任务，
                     * 所有的耗时任务都是在子线程中执行
                     */
                    doTask(lastTask);
                } catch (Exception e) {
                    // TODO: handle exception
                    Log.e(TAG,"TaskManager=====>执行任务发生了异常，信息为：" + e.getLocalizedMessage());
                    e.printStackTrace();
                    /**
                     * 处理异常的回调
                     */
                    lastTask.setStatus(Task.ERROR);
                    lastTask.setException(e.getLocalizedMessage());
                    if (lastTask.isMainThread()) {
                        Message msg = Message.obtain();
                        msg.what = COMMON_EXCUTE_TASK_TYPE;
                        msg.obj = lastTask;
                        handler.sendMessage(msg);
                    } else {
                        doCommonHandler(lastTask);
                    }
                }
            } else {
                try {
                    synchronized (allTask) {
                        allTask.wait();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 根据不同的Task,执行相应的任务
     *
     * 这个是真正开始执行异步任务的地方，
     * 即调用需要在子线程执行的代码==>task.doTask()
     *
     * @param task Task对象
     */
    private void doTask(Task task) {
        Task result = task.doTask();
        /**
         *
         * 开始执行的Task和最后得到的Task是同一个的时候，才会进行回调，
         * 否则不进行回调(保证在回调得到数据的时候知道是哪一个Task,以便进行强转)
         *
         *
         * 没有UniqueID相当于不需要回调
         *
         */
        if (result != null && task == result && result.getUniqueID() != 0) {
            /**
             *  发送当前消息,更新UI(把数据回调到界面),
             *  下面不用做任何的发送消息，
             *  只在这一个地方发送就行，否者会发生错误！
             */

            if (result.isMainThread()) {
                Message msg = Message.obtain();
                msg.what = COMMON_EXCUTE_TASK_TYPE;
                msg.obj = result;
                handler.sendMessage(msg);
            } else {
                doCommonHandler(task);
            }
        } else {
            uniqueListenerList.remove(task.getUniqueID());
        }
    }

    /**
     * 真正的回调操作，所有的任务在这里
     * 把数据回调到主界面
     *
     * @param task Task对象
     */
    private void doCommonHandler(Task task) {
        long start = System.currentTimeMillis();
        if (task != null && uniqueListenerList.containsKey(task.getUniqueID())) {
            try {
                /**
                 * 回调整个Task数据
                 * 然后可以回调方法中去直接更新UI
                 */
                ((OnResult) uniqueListenerList.get(task.getUniqueID())).onFinish(task);
                /**
                 * 回调完成移除CallBack对象
                 */
                uniqueListenerList.remove(task.getUniqueID());
            } catch (Exception e) {
                // TODO: handle exception
                Log.e(TAG,"TaskManager出错了-->"+task.getClass().getSimpleName()+":" + e.getMessage());
                e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();
        Log.i(TAG,"执行回调doCommonHandler 耗时：" + (end - start));
    }
    /**
     * 顺序执行耗时的操作
     *
     * @param runnable 对象
     */
    public void exec(Runnable runnable) {
        singlePool.execute(runnable);
    }
    /**
     * 顺序执行耗时的操作
     *
     * @param runnable 对象
     * @param delay    延迟执行的时间，单位毫秒
     */
    public void exec(Runnable runnable, long delay) {
        singlePool.schedule(runnable, delay, TimeUnit.MILLISECONDS);
    }

    /**
     * 顺序执行耗时的操作
     *
     * @param runnable 对象
     * @param delay    延迟执行的时间
     * @param timeUnit 时间单位
     */
    public void exec(Runnable runnable, long delay, TimeUnit timeUnit) {
        singlePool.schedule(runnable, delay, timeUnit);
    }

    /**
     *  以上一个任务开始时计时，传入等待时间
     *  检测上一个任务是否执行完毕，则当前任
     *  务立即执行，如果上一个任务没有执行完
     *  毕，则需要等上一个任务执行完毕后立即执行。
     * @param runnable
     * @param delay
     * @param period
     * @param timeUnit
     */
    public void scheduleAtFixedRate(Runnable runnable, long delay, long period, TimeUnit timeUnit) {
        singlePool.scheduleAtFixedRate(runnable, delay, period, timeUnit);
    }
    public void scheduleAtFixedRate(Runnable runnable, long delay, long period) {
        singlePool.scheduleAtFixedRate(runnable, delay, period, TimeUnit.MILLISECONDS);
    }
    public void scheduleAtFixedRate(Runnable runnable, long period) {
        singlePool.scheduleAtFixedRate(runnable, 0, period, TimeUnit.MILLISECONDS);
    }
    public interface OnResult {
        void  onFinish(Task task);
    }
}
